﻿using Airthmetic;
using BaseTool;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace StructuredLightSystemUI
{
    /// <summary>
    /// 标定页面
    /// </summary>
    public partial class ConCalibration : UserControl
    {
        /// <summary> 投影仪工具 </summary>
        private ProjectorTool proTool { get { return MyGlobal.ProjectorTool; } }
        /// <summary> 相机工具 </summary>
        private CameraTool camTool { get { return MyGlobal.CameraTool; } }
        /// <summary> 标定工具 </summary>
        private CalibrationTool tool { get { return MyGlobal.CalibrationTool; } }

        /// <summary> 标定文件夹列表 </summary>
        private List<string> calist = new List<string>();
        /// <summary> 重建文件夹列表 </summary>
        private List<string> relist = new List<string>();

        /// <summary> 锁：观察 </summary>
        private readonly object lockObserve = new object();
        /// <summary> 正在刷新控件 </summary>
        private bool isRefreshControl = false;
        /// <summary> 标定后台线程 </summary>
        private Thread threadCalibration;


        #region 构造、初始化
        /// <summary>
        /// 构造
        /// </summary>
        public ConCalibration()
        {
            InitializeComponent();
        }

        //加载时
        private void ConCalibration_Load(object sender, EventArgs e)
        {
            Machine.ChangeProductAction += ChangeProduct;
        }

        /// <summary> 打开用户控件 </summary>
        public void OpenControl()
        {
            RefreshControl(true);
        }

        /// <summary> 关闭用户控件 </summary>
        public void CloseControl()
        {
            Observe(0);
            DestroyThread();
        }
        /// <summary>
        /// 刷新控件 - 重新连接
        /// </summary>
        private void ChangeProduct()
        {
            RefreshControl(true);
        }
        #endregion

        /// <summary>
        /// 刷新控件
        /// </summary>
        /// <param name="isReCon"> 是否重新连接 </param>
        private void RefreshControl(bool isReCon = false)
        {
            isRefreshControl = true;

            try
            {
                //重新连接相机投影仪
                if (isReCon)
                    ReConnectionAll();

                //采集数据
                l_camExposure.Text = $"曝光：{camTool.Param.Read.ExposureTime}us";

                ProjectorConTrigger projector = proTool.Param.calibrationParam;
                l_proCount.Text = $"数量：{projector.Count}";
                l_proImageIndex.Text = $"图像序号：{projector.GetImageIndex}";
                l_proFrame.Text = $"帧率：{projector.Frame.Frequency.ToString("f1")} 帧/s （{projector.Frame.ExposureTime_ms.ToString("f1")}ms/张）";
                l_proTotalTime.Text = $"总时间：{(projector.Frame.ExposureTime_ms * projector.Count / 1000.0).ToString("f1")} s";

                //标定计算
                clb_Cali.Items.Clear();
                clb_Cali.ClearSelected();

                calist.Clear();
                relist.Clear();

                ImageTool.LoadImage(out calist, out relist);

                for (int i = 0; i < calist.Count; i++)
                {
                    FileInfo fi = new FileInfo(calist[i]);
                    clb_Cali.Items.Add($"[{i + 1}]   {fi.Name}   ({fi.FullName})");
                }

                nud_Threshold.Value = tool.Param.Threshold;
                nud_BlackLightPowerRatio.Value = (decimal)tool.Param.BlackLightPowerRatio;
                nud_MinDirectLightComponent.Value = tool.Param.MinDirectLightComponent;
                nud_HomographyWindow.Value = tool.Param.HomographyWindow;

                //棋盘格参数
                CheckerboardImageTool checkerboard = MyGlobal.CheckerboardImageTool;
                tool.Param.PatternSize = (int)checkerboard.OneGridActualLength;
                tool.Param.CornerCount = new OpenCvSharp.Size(
                   checkerboard.BoardSizeWidth - 1, checkerboard.BoardSizeHeight - 1);
                l_patternCorner.Text = $"棋盘格角点：{tool.Param.CornerCount.Width + 1} * {tool.Param.CornerCount.Height + 1} ({tool.Param.PatternSize}mm)";

                ////条纹光栅参数
                //GrayCodeParam grayCodeParam = MyGlobal.StripedGratingImageTool.GrayCode;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
            }
            finally
            {
                isRefreshControl = false;
            }
        }
        /// <summary>
        /// 重新连接相机投影仪
        /// </summary>
        private void ReConnectionAll()
        {
            try
            {
                //相机
                if (camTool.IsGrabbing)
                    camTool.StopGrab();
                if (camTool.IsConnected)
                    camTool.CloseCamera();
                camTool.ConnectedCamera();
                camTool.SetParam(camTool.Param.Calibration);

                //投影仪
                if (proTool.IsOpen)
                    proTool.Close();
                proTool.Open();

                proTool.Send(proTool.GetData_SwitchLED(false));
                proTool.Send(proTool.GetData_WorkPattern(false, false));
                proTool.Send(proTool.GetData_BrightnessSetting((byte)nud_proBrightness.Value));

            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
            }
        }

        #region 采集数据

        //观察
        private void b_observe_Click(object sender, EventArgs e)
        {
            Observe(1);
        }
        //关闭
        private void b_close_Click(object sender, EventArgs e)
        {
            Observe(0);
        }
        //采集
        private void b_capture_Click(object sender, EventArgs e)
        {
            Observe(2);
        }
        //亮度参数更改
        private void nud_proBrightness_ValueChanged(object sender, EventArgs e)
        {
            //投影仪
            if (proTool.IsOpen)
                proTool.Send(proTool.GetData_BrightnessSetting((byte)nud_proBrightness.Value));
        }

        /// <summary>
        /// 观察
        /// </summary>
        /// <param name="index"></param>
        private void Observe(int index)
        {
            if (!camTool.IsConnected || !proTool.IsOpen)
                return;

            lock (lockObserve)
            {
                switch (index)
                {
                    //停止观察
                    case 0:
                        {
                            if (camTool.IsGrabbing)
                                camTool.StopGrab();

                            proTool.Send(proTool.GetData_SwitchLED(false));
                            RefreshControl();
                            return;
                        }
                    //观察
                    case 1:
                        {
                            if (camTool.IsGrabbing)
                                Observe(0);

                            camTool.Capture = new CameraCapture();
                            camTool.Capture.CaptureMode = CameraCaptureMode.连续采集指针模式;
                            camTool.Capture.HandlePictureBox = conPictureBox1.Handle;

                            camTool.SetParam(camTool.Param.Calibration);
                            camTool.SetContinuesMode();
                            camTool.StartGrab();

                            proTool.Send(proTool.GetData_SwitchLED(true));
                            break;
                        }

                    //采集
                    case 2:
                        {
                            if (camTool.IsGrabbing)
                                Observe(0);

                            DateTime now = DateTime.Now;

                            //1 设置参数
                            int ImageCount = proTool.Param.calibrationParam.Count;
                            camTool.Capture = new CameraCapture();
                            camTool.Capture.CaptureMode = CameraCaptureMode.硬触发采集异步模式;
                            camTool.Capture.ReceiveImageMaxCount = ImageCount;

                            camTool.SetParam(camTool.Param.Calibration);
                            camTool.SetHardTrigger(camTool.Param.Calibration);
                            camTool.StartGrab();
                            camTool.ClearReceiveImage();

                            proTool.Send(proTool.GetData_SwitchLED(true));
                            proTool.Send(proTool.GetData_ContinuousDisplayImage(proTool.Param.calibrationParam));

                            //采集数据
                            //int OverTime = ImageCount * camTool.Write.OverTime;
                            int OverTime = 3000;
                            Stopwatch sw = Stopwatch.StartNew();
                            while (sw.ElapsedMilliseconds <= OverTime &&
                                camTool.Capture.ReceiveImageCount < ImageCount)
                            {
                                Thread.Sleep(10);
                                SetStatus(
                                    1.0 * camTool.Capture.ReceiveImageCount / ImageCount,
                                    $"采集数据：{camTool.Capture.ReceiveImageCount}/{ImageCount}");
                            }

                            //关闭
                            camTool.StopGrab();
                            proTool.Send(proTool.GetData_SwitchLED(false));

                            //保存数据
                            DirectoryInfo di = new DirectoryInfo(PathTool.DataCaptureCalibrationPath(now));
                            if (!di.Exists)
                                di.Create();
                            int sum = 0;
                            while (camTool.Capture.ReceiveImageCount > 0)
                            {
                                Mat temp;
                                if (!camTool.Capture.ReceiveImage.TryDequeue(out temp)) break;
                                temp.SaveImage($"{di.FullName}{(sum + 1).ToString().PadLeft(2, '0')}.bmp");
                                temp.Dispose();
                                sum++;
                                SetStatus(
                                   1.0 * sum / ImageCount,
                                   $"保存数据：{sum}/{ImageCount}");
                            }

                            //日志
                            bool isok = sum >= ImageCount;
                            if (isok)
                                Log.SaveLog($"采集数据成功，共采集数据{sum} / {ImageCount}个，耗时：{sw.ElapsedMilliseconds}ms。", LogType.Run, Color.Black);
                            else
                                Log.SaveLog($"采集数据失败，共采集数据{sum} / {ImageCount}个，耗时：{sw.ElapsedMilliseconds}ms。", LogType.Run, Color.Red);

                            //刷新
                            RefreshControl(false);
                            break;
                        }

                }
            }
        }
        /// <summary>
        /// 设置状态
        /// </summary>
        /// <param name="per"></param>
        /// <param name="status"></param>
        private void SetStatus(double per, string status)
        {
            Machine.StatusBarAction?.Invoke(per, status, Color.Black);
            l_observeStatus.Invoke((MethodInvoker)delegate
            {
                l_observeStatus.Text = $"状态：\r\n{status}";
                l_observeStatus.Refresh();
            });
        }

        #endregion

        #region 标定计算
        //点击显示
        private void clb_Cali_SelectedIndexChanged(object sender, EventArgs e)
        {
            string imageDir = GetFileName(clb_Cali.SelectedIndex);
            if (!string.IsNullOrEmpty(imageDir))
                ShowImage(imageDir);
        }
        //全选
        private void 全选ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            clb_Cali.ClearSelected();
            for (int i = 0; i < clb_Cali.Items.Count; i++)
            {
                clb_Cali.SetItemChecked(i, true);
            }
        }
        //全不选
        private void 全不选ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            clb_Cali.ClearSelected();
            for (int i = 0; i < clb_Cali.Items.Count; i++)
            {
                clb_Cali.SetItemChecked(i, false);
            }
        }
        //反选
        private void 反选ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            clb_Cali.ClearSelected();
            for (int i = 0; i < clb_Cali.Items.Count; i++)
            {
                clb_Cali.SetItemChecked(i, !clb_Cali.GetItemChecked(i));
            }
        }
        /// <summary> 
        /// 获取文件名 
        /// </summary>
        private string GetFileName(int ItemIndex)
        {
            return calist.Count > ItemIndex && ItemIndex != -1 ? calist[ItemIndex] : null;
        }
        /// <summary> 
        /// 显示图像
        /// </summary>
        private void ShowImage(string imageDir)
        {
            conPictureBox1.Invoke((MethodInvoker)delegate
            {
                if (!Directory.Exists(imageDir))
                    return;
                string[] fis = Directory.GetFiles(imageDir);

                foreach (string f in fis)
                {
                    FileInfo fi = new FileInfo(f);
                    if (fi.Extension != ".png" && fi.Extension != ".jpg" && fi.Extension != ".bmp")
                        continue;
                    if (!fi.Name.Contains("01"))
                        continue;

                    Mat mat = Cv2.ImRead(f);
                    conPictureBox1.ShowImage = mat.ToBitmap();
                    break;
                }
            });
        }
        //参数变化
        private void nud_Cali_ValueChanged(object sender, EventArgs e)
        {
            if (isRefreshControl)
                return;
            try
            {
                tool.Param.Threshold = (int)nud_Threshold.Value;
                tool.Param.BlackLightPowerRatio = (double)nud_BlackLightPowerRatio.Value;
                tool.Param.MinDirectLightComponent = (byte)nud_MinDirectLightComponent.Value;
                tool.Param.HomographyWindow = (int)nud_HomographyWindow.Value;

                tool.Save();
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
            }
        }
        //标定
        private void b_Cali_Click(object sender, EventArgs e)
        {
            SetEnable(false);

            //异步线程启动标定程序
            if (threadCalibration == null ||
                threadCalibration.ThreadState == System.Threading.ThreadState.Stopped)
            {
                Machine.StatusBarAction?.Invoke(0, DateTime.Now.ToString(), Color.Black);
                threadCalibration = new Thread(Calibration);
                threadCalibration.IsBackground = true;
                threadCalibration.Start();
            }

            SetEnable(true);
        }


        /// <summary>
        /// 设置控件使能
        /// </summary>
        /// <param name="isEnable"></param>
        private void SetEnable(bool isEnable)
        {
            groupBox3.Enabled = isEnable;
            groupBox4.Enabled = isEnable;
            groupBox5.Enabled = isEnable;
        }
        /// <summary> 
        /// 销毁线程
        /// </summary>
        public void DestroyThread()
        {
            if (threadCalibration != null &&
                threadCalibration.ThreadState == System.Threading.ThreadState.Running)
                threadCalibration.Abort();
        }
        /// <summary> 
        /// 标定 
        /// </summary>
        private void Calibration()
        {
            CalibrationInput input = new CalibrationInput();
            clb_Cali.Invoke((MethodInvoker)delegate
            {
                foreach (int index in clb_Cali.CheckedIndices)
                {
                    input.capImagePath.Add(calist[index]);
                }
            });

            //条纹光栅参数
            GrayCodeParam grayCodeParam = MyGlobal.StripedGratingImageTool.GrayCode;
            input.PatternCount = 2 + (grayCodeParam.HorizontalDepth + grayCodeParam.VerticalDepth) * 2;
            input.HorizontalDepth = grayCodeParam.HorizontalDepth;
            input.VerticalDepth = grayCodeParam.VerticalDepth;

            tool.Calibration(input);

            RefreshResult();
        }
        /// <summary> 
        /// 标定完成后刷新结果 
        /// </summary>
        private void RefreshResult()
        {
            l_Result.Invoke((MethodInvoker)delegate
            {
                l_Result.Text = "相机误差：".PadRight(10) + tool.Result.cam_error + "\r\n" +
                                "投影仪误差：".PadRight(10) + tool.Result.proj_error + "\r\n" +
                                "系统误差：".PadRight(10) + tool.Result.stereo_error;
            });
        }
        #endregion


    }
}
